#ifndef __baker_VectorOp_h__
#define __baker_VectorOp_h__

namespace baker
{

namespace VecOp1
{
    // comparing two vectors
    template< int N >
    static inline bool _cmp ( real const * lhs
                            , real const * rhs
                            )
    {
        return ( fabsf( lhs[ N-1 ] - rhs[ N-1 ] ) < EPSILON )
            && ( _cmp< N-1 >( lhs, rhs ) );
    }

    template<>
    static inline bool _cmp< 1 >( real const * lhs
                                , real const * rhs
                                )
    {
        return ( fabsf( lhs[ 0 ] - rhs[ 0 ] ) < EPSILON );
    }

    // adding two vectors
    template< int N >
    static inline void _add ( real * result
                            , real const * lhs
                            , real const * rhs
                            )
    {
        result[ N-1 ] = lhs[ N-1 ] + rhs[ N-1 ];

        _add< N-1 >( result, lhs, rhs );
    }

    template<>
    static inline void _add< 1 >( real * result
                                , real const * lhs
                                , real const * rhs
                                )
    {
        result[ 0 ] = lhs[ 0 ] + rhs[ 0 ];
    }

    // subtracting two vectors
    template< int N >
    static inline void _sub ( real * result
                            , real const * lhs
                            , real const * rhs
                            )
    {
        result[ N-1 ] = lhs[ N-1 ] - rhs[ N-1 ];

        _sub< N-1 >( result, lhs, rhs );
    }

    template<>
    static inline void _sub< 1 >( real * result
                                , real const * lhs
                                , real const * rhs
                                )
    {
        result[ 0 ] = lhs[ 0 ] - rhs[ 0 ];
    }

    // multiplying two vectors
    template< int N >
    static inline void _mul ( real * result
                            , real const * lhs
                            , real const * rhs
                            )
    {
        result[ N-1 ] = lhs[ N-1 ] * rhs[ N-1 ];

        _mul< N-1 >( result, lhs, rhs );
    }

    template<>
    static inline void _mul< 1 >( real * result
                                , real const * lhs
                                , real const * rhs
                                )
    {
        result[ 0 ] = lhs[ 0 ] * rhs[ 0 ];
    }

    // multiplying a vector and a scalar
    template< int N >
    static inline void _mul ( real * result
                            , real const * vec
                            , real s
                            )
    {
        result[ N-1 ] = vec[ N-1 ] * s;

        _mul< N-1 >( result, vec, s );
    }

    template<>
    static inline void _mul< 1 >( real * result
                                , real const * vec
                                , real s
                                )
    {
        result[ 0 ] = vec[ 0 ] * s;
    }

    // computing the dot-product of two vectors
    template< int N >
    static inline real _dot ( real const * lhs
                            , real const * rhs
                            )
    {
        return ( lhs[ N - 1 ] * rhs[ N - 1 ] ) + _dot< N - 1 >( lhs, rhs );
    }

    template<>
    static inline real _dot< 1 >( real const * lhs
                                , real const * rhs
                                )
    {
        return lhs[ 0 ] * rhs[ 0 ];
    }
}


    namespace VecOp = VecOp1;

    /** Operator == overloading
        @return a == b
    */
    template< typename T >
    static inline bool operator == ( const T & a
                                   , const T & b
                                   )
    {
        return VecOp::_cmp< T::CNT >( a.v, b.v );
    }

    /** Operator + overloading
        @return a + b
    */
    template< typename T >
    static inline T operator + ( const T & a
                               , const T & b
                               )
    {
        T result;

        VecOp::_add< T::CNT >( result.v, a.v, b.v );

        return result;
    }

    /** Operator - overloading
        @return a - b
    */
    template< typename T >
    static inline T operator - ( const T & a
                               , const T & b
                               )
    {
        T result;

        VecOp::_sub< T::CNT >( result.v, a.v, b.v );

        return result;
    }

    /** Operator - overloading
        @return -v
    */
    template< typename T >
    static inline T operator - ( const T & v )
    {
        T result;

        VecOp::_mul< T::CNT >( result.v, v.v, -1.0 );

        return result;
    }

    /** Operator * overloading
        @return The product of a and b such that product[i] = a[i] * b[i]
    */
    template< typename T >
    static inline T operator * ( const T & a
                               , const T & b
                               )
    {
        T result;

        VecOp::_mul< T::CNT >( result.v, a.v, b.v );

        return result;
    }

    /** Operator * overloading
        @return The product of v and s such that product[i] = v[i] * s
    */
    template< typename T >
    static inline T operator * ( const T & v
                               , real s
                               )
    {
        T result;

        VecOp::_mul< T::CNT >( result.v, v.v, s );

        return result;
    }

    /** Operator * overloading
        @return The product of v and s such that product[i] = v[i] * s
    */
    template< typename T >
    static inline T operator * ( real s
                               , const T & v
                               )
    {
        T result;

        VecOp::_mul< T::CNT >( result.v, v.v, s );

        return result;
    }

    /** Returns the squared-length if a Vector
    */
    template< typename T >
    static inline real lengthSq( const T & v )
    {
        return VecOp::_dot< T::CNT >( v.v, v.v );
    }

    /** Returns the length if a Vector
    */
    template< typename T >
    static inline real length( const T & v )
    {
        return sqrtf( VecOp::_dot< T::CNT >( v.v, v.v ) );
    }

    /** Normalize a Vector
    */
    template< typename T >
    static inline void normalize( T & v )
    {
        real invL = 1.0 / length( v );

        VecOp::_mul< T::CNT >( v.v, v.v, invL );
    }

    /** Returns normalized copy of a Vector
        (i.e. the value of input Vector will NOT be affected).
    */
    template< typename T >
    inline T normalizedCopy( const T & v )
    {
        real invL = 1.0 / length( v );

        T result;

        VecOp::_mul< T::CNT >( result.v, v.v, invL );

        return result;
    }

    /** Compute the scalar-product (i.e. dot-product) of a and b
        @return The scalar-product of a and b
    */
    template< typename T >
    static inline real dot ( const T & a
                           , const T & b
                           )
    {
        return VecOp::_dot< T::CNT >( a.v, b.v );
    }

    /** Compute the cross-product (i.e. dot-product) of a and b given by the right-hand rule<br>
        Note: i x j = k; j x k = i; k x i = j
        @return The cross-product of a and b ( i.e. a x b )
    */
    static inline Vec3 cross ( const Vec3 & a
                             , const Vec3 & b
                             )
    {
        return Vec3( a.v[1] * b.v[2] - a.v[2] * b.v[1]
                   , a.v[2] * b.v[0] - a.v[0] * b.v[2]
                   , a.v[0] * b.v[1] - a.v[1] * b.v[0]
                   );
    }

}

#endif  // __baker_VectorOp_h__
